home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_56 / dmastep8.asm < prev    next >
Assembly Source File  |  1995-01-01  |  17KB  |  440 lines

  1. ;══════════════════════════════════════════════════════════════════════════════
  2. ; Play with Cxh/Bxh commands on SoundBlaster 16/16ASP (here 8/mono/signed)
  3. ;   André Baresel (with some help from Craig Jackson)
  4. ;──────────────────────────────────────────────────────────────────────────────
  5. ; STATUS: DOES WORK ON SB16
  6. ;──────────────────────────────────────────────────────────────────────────────
  7. ; Requirements: 80286, SoundBlaster 16/16ASP (see BASEADDR,DMA channel,IRQ number)
  8. ; Resolutions : 8-bit / 4..44khz
  9. ; Parameters  : none
  10. ; Note        : Before compiling this call first "VOC2RAW TEST1.VOC /I /S"
  11. ;         to creat "TEST1.INC" in this case a file with 8bit mono signed data
  12. ;
  13. ; ■ DSP command 40h  ... set sample rate
  14. ; ■ DSP command D0h  ... Halt Autoinit 8 bit DMA operation
  15. ; ■ DSP command D4h  ... Continue Autoinit 8 bit DMA operation
  16. ; ■ DSP command C6h 10h ... autoinit 8 bit mono data with sign
  17. ;
  18.  
  19. .MODEL small
  20. .286
  21. ; CONSTANTS ───────────────────────────────────────────────────────────────────
  22.  
  23. ; SoundBlaster SETUP
  24. BASEADDR           EQU 0220h       ;SoundBlaster base address
  25. IRQ7               EQU 15          ;SoundBlaster IRQ
  26. DMAchannel         EQU 1           ;SoundBlaster DMA channel
  27.  
  28. ; PIC MASKS FOR MASK/DEMASK IRQ
  29. PICANDMASK         EQU 01111111b   ;'AND' PIC mask for clear IRQ7
  30. PICORMASK          EQU 10000000b   ;'OR' PIC mask for set IRQ7
  31.  
  32. ; DMA CONTROLLER REGISTERS :
  33. WRITEMASK          EQU 00ah         ;WRITE MASK REGISTER
  34. WRITEMODE          EQU 00bh         ;WRITE MODE REGISTER
  35. CLEARFLIPFLOP      EQU 00ch
  36. PAGE_CHN           EQU 083h         ;PAGE REGISTER FOR DMAchannel 1
  37. BASE_CHN           EQU 002h         ;BASEADDRESS REGISTER DMAchannel 1
  38. COUNT_CHN          EQU 003h         ;COUNT REGISTER DMAchannel 1
  39.  
  40. ; SAMPLERATE : (if you change it pay attention to maximum samplerate)
  41. TIMECONST          EQU 165          ; = 10989 Hz (256-1000000/11000)
  42.  
  43. ; DMA WRITE MODE
  44. WANTEDMODE         EQU 01011000b    ; singlemode, autoinit, readmode
  45.  
  46. ;──────────────────────────────────────────────────────────────────────────────
  47. ; MACRO DEFINITIONs
  48. ;──────────────────────────────────────────────────────────────────────────────
  49. STARTUP                 MACRO
  50. ; MASM 5.x COMPATIBILITY
  51. __start:                mov     ax,DGROUP
  52.                         mov     ds,ax
  53.                         mov     bx,ss
  54.                         sub     bx,ax
  55.                         shl     bx,004h
  56.                         mov     ss,ax
  57.                         add     sp,bx
  58. ENDM
  59.  
  60. WAITWRITE               MACRO
  61. LOCAL                   loopWait,endloop
  62. ;          Arguments : DX = Status port (BASEADDR+0Ch)
  63. ;          Returns   : n/a
  64. ;          Destroys  : AL
  65.  
  66.                         push    cx
  67.                         xor     cx,cx           ; need that for slow SBs !
  68. loopWait:               dec     cx
  69.                         jz      endloop
  70.                         in      al,dx           ; AL = WRITE COMMAND STATUS
  71.                         or      al,al
  72.                         js      loopWait        ; Jump if bit7=1 - writing not allowed
  73. endloop:                pop     cx
  74. ENDM
  75.  
  76. WAITREAD                MACRO
  77. LOCAL                   loopWait,endloop
  78. ;          Arguments : DX = Status port   (normaly BASEADDR+0Eh)
  79. ;          Returns   : n/a
  80. ;          Destroys  : AL
  81.  
  82.                         push    cx
  83.                         xor     cx,cx           ; need that for slow SBs !
  84. loopWait:               dec     cx
  85.                         jz      endloop
  86.                         in      al,dx           ; AL = DATA AVAILABLE STATUS
  87.                         or      al,al
  88.                         jns     loopWait        ; Jump if bit7=0 - no data available
  89. endloop:                pop     cx
  90. ENDM
  91.  
  92. RESET_DSP               MACRO
  93. local                   SBthere
  94. ;          Arguments : n/a
  95. ;          Returns   : n/a
  96. ;          Destroys  : DX,AL
  97.  
  98.                         mov      dx,BASEADDR+06h
  99.                         mov      al,1
  100.                         out      dx,al          ; start DSP reset
  101.  
  102.                         in       al,dx
  103.                         in       al,dx
  104.                         in       al,dx
  105.                         in       al,dx          ; wait 3 µsec
  106.  
  107.                         xor      al,al
  108.                         out      dx,al          ; end DSP Reset
  109.  
  110.                         add      dx,08h         ; dx = DSP DATA AVAILABLE
  111.                         WAITREAD
  112.                         sub      dx,4           ; dx = DSP Read Data
  113.                         in       al,dx
  114.                         cmp      al,0aah        ; if there is a SB then it returns 0AAh
  115.                         je       SBthere
  116.                         jmp      RESET_ERROR    ; No SB - exit program
  117. SBthere:
  118. ENDM
  119. ;─── End of Macrodefinitions ──────────────────────────────────────────────────
  120.  
  121. .STACK 100h
  122.  
  123. .DATA
  124. ;──────────────────────────────────────────────────────────────────────────────
  125. ; Creat TEST1.INC with calling "VOC2RAW TEST1.VOC /I /S" or creat your own
  126. ; textfile with sampledata -> signed_data_8bit = unsigned_data_8bit-128
  127.  
  128. SAMPLEBUFFER LABEL BYTE
  129.     INCLUDE TEST1.INC
  130. SAMPLEBUFFEREND LABEL BYTE
  131.  
  132.     part                db 1
  133.  
  134.     information         db 13,10,'DMASTEP8.EXE - play 8bit mono data with sign (that does only work on a'
  135.                         db 13,10,'SB16/SB16ASP) signed data : -128..127 (e.g. amiga samples)'
  136.                         db 13,10,'Pause playing with key "p" and continue it then with any key.'
  137.                         db 13,10,'Stop playing with <ESC>.','$'
  138.     memerror            db 13,10,'Not enough memory to creat the DMA buffer','$'
  139.     txtpart0            db 13,10,'playing part 0','$'
  140.     txtpart1            db 13,10,'playing part 1','$'
  141.     sberror             db 13,10,'No SoundBlaster at this BASEADDR ! PROGRAM HALTED.','$'
  142.  
  143.     OLDInterruptSEG     dw ?
  144.     OLDInterruptOFS     dw ?
  145.  
  146.     DMAbufferOFS        dw ?
  147.     DMAbufferPage       db ?
  148.  
  149.     SAMPLEBUFFERLENGTH = offset SAMPLEBUFFEREND - offset SAMPLEBUFFER
  150. ;──────────────────────────────────────────────────────────────────────────────
  151. .CODE
  152.  STARTUP
  153.  
  154.            ; FIRST FREE NOT USED MEMORY :
  155.            mov     bx,ss
  156.            mov     ax,es
  157.            sub     bx,ax
  158.  
  159.            mov     ax,sp
  160.            add     ax,15
  161.  
  162.            shr     ax,4
  163.  
  164.            add     bx,ax
  165.            mov     ah,04ah
  166.            int     21h
  167.  
  168.            ; NOW ALLOCATE DMABUFFER
  169.            mov     bx,SAMPLEBufferlength+15
  170.            shr     bx,3            ; cx = samplebufferlength*2/16
  171.                                    ; (count of 16byte blocks)
  172.            mov     ah,48h
  173.            int     21h
  174.            jnc     enoughmem       ; ok got the memory
  175.            mov     dx,offset memerror
  176.            mov     ah,9
  177.            int     21h             ; WRITE MSG 2 SCRN THAT THERE'S NOT ENOUGH MEM
  178.            jmp     return2dos
  179. enoughmem: ; AX = segment of DMA buffer / offset = 0
  180.  
  181. ;──────────────────────────────────────────────────────────────────────────────
  182. ; calculate page and offset for DMAcontroller :
  183. ;
  184. ; segment*16+offset = 20bit memory location -> upper 4 bits  = page
  185. ;                                              lower 16 bits = offset
  186. ;──────────────────────────────────────────────────────────────────────────────
  187.            rol     ax,4
  188.            mov     bl,al
  189.            and     bl,00fh
  190.            and     al,0f0h
  191.            mov     [DMABufferOFS],ax
  192.            mov     [DMAbufferPage],bl
  193. ;──────────────────────────────────────────────────────────────────────────────
  194. ; check for DMApage override :
  195. ; ... problem: DMA controller separates memory into 64KB pages, you can only
  196. ; transfer data is placed in one page - no page overrides are allowed
  197. ;──────────────────────────────────────────────────────────────────────────────
  198. ; To solve that :
  199. ; creat a DMA buffer with double size you want - if the first part is placed
  200. ; on a page border the second part is for sure not
  201. ;──────────────────────────────────────────────────────────────────────────────
  202.            mov     cx,SAMPLEBUFFERLENGTH
  203.            neg     ax          ; ax = 65536 - ax   (bytes left to DMA page border)
  204.            cmp     ax,cx
  205.            ja      nooverride
  206.  
  207.            ; USE SECOND PART :
  208.            neg     ax               ; ax = offset first data
  209.            add     ax,cx            ; use second part
  210.            inc     ax             ; cx+1 bytes to next part ;)
  211.            mov     [DMABufferOFS],ax
  212.            inc     [DMABufferPage]  ; 2nd part is on next page !
  213. nooverride:
  214.  
  215. ;──────────────────────────────────────────────────────────────────────────────
  216. ; Now move the sampledata into the DMAbuffer
  217. ; in a MODplayer you'll do that in the IRQ,
  218. ; but in our case we only repeat the same sound in the DMAbuffer
  219. ;──────────────────────────────────────────────────────────────────────────────
  220.            ; FIRST CALCULATE THE DOS SEG/OFS FROM DMAPage/OFS
  221.            mov     al,byte ptr [DMABufferOFS]
  222.            and     al,0fh
  223.            xor     ah,ah
  224.            mov     di,ax       ; di = offset of DMAbuffer
  225.            mov     ax,[DMABufferOFS]
  226.            and     al,0f0h
  227.            or      al,[DMABufferPage]
  228.            ror     ax,4
  229.            mov     es,ax       ; es = segment of DMABuffer
  230.            mov     si,offset samplebuffer
  231.  
  232.            ;NOW COPY IT :
  233.            ; DS:SI - samples in dataseg
  234.            ; ES:DI - DMABuffer
  235.            ;
  236.            rep movsb
  237.  
  238.            RESET_DSP
  239.  
  240.            ; WRITE INFOMRATION TO SCREEN :
  241. mov     dx,offset information
  242.            mov     ah,9
  243.            int     21h                  ; write program information to screen
  244.  
  245.            ; ENABLE SB SPEAKERS (for all SBs <SB16)
  246.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  247.            WAITWRITE
  248.            mov     al,0D1h                     ; AL = Enable speaker
  249.            out     dx,al                       ; Output: DSP Write Data or Command
  250.  
  251.            ; SETUP IRQ :
  252.            xor     ax,ax
  253.            mov     es,ax                       ; es to page 0 (Interrupt table)
  254.            mov     si,IRQ7*4                   ; si = position in interrupt table
  255.  
  256.            ; DISABLE IRQ (if it was enabled somehow)
  257.            in      al,021h
  258.            and     al,PICANDMASK               ; SET MASK REGISTER BIT TO DISABLE INTERRUPT
  259.            out     021h,al
  260.  
  261.            ; CHANGE POINTER IN INTERRUPT TABLE
  262.            mov     ax,es:[si]
  263.            mov     [OLDInterruptOFS],ax        ; save offset of old interupt vector for restoring
  264.            mov     ax,OFFSET OWN_IRQ
  265.            mov     es:[si],ax                  ; set offset of new interrupt routine
  266.            mov     ax,es:[si+2]
  267.            mov     [OLDInterruptSEG],ax        ; save segment of old interupt vector for restoring
  268.            mov     ax,cs
  269.            mov     es:[si+2],ax                ; set segment of new interrupt routine
  270.  
  271.            ; CHANGE PIC MASK :
  272.            in      al,021h
  273.            and     al,PICANDMASK   ; CLEAR MASK REGISTER BIT TO ENABLE INTERRUPT
  274.            out     021h,al
  275.  
  276.  
  277.            mov     cx,SAMPLEBUFFERLENGTH-1
  278. ;──────────────────────────────────────────────────────────────────────────────
  279. ; Setup DMA-controller :
  280. ;
  281. ; 1st  MASK DMA CHANNEL
  282. ;
  283.            mov     al,DMAchannel
  284.            add     al,4
  285.            out     WRITEMASK,al
  286. ;──────────────────────────────────────────────────────────────────────────────
  287. ; 2nd  CLEAR FLIPFLOP
  288. ;
  289.            out     CLEARFLIPFLOP,al
  290. ;──────────────────────────────────────────────────────────────────────────────
  291. ; 3rd  WRITE TRANSFER MODE
  292. ;
  293.            mov     al,WANTEDMODE
  294.            add     al,DMAchannel
  295.            out     WRITEMODE,al
  296. ;──────────────────────────────────────────────────────────────────────────────
  297. ; 4th  WRITE PAGE NUMBER
  298. ;
  299.            mov     al,[DMAbufferPage]
  300.            out     PAGE_CHN,al
  301. ;──────────────────────────────────────────────────────────────────────────────
  302. ; 5th  WRITE DMA BASEADDRESS
  303. ;
  304.            mov     ax,[DMABufferOFS]
  305.            out     BASE_CHN,al
  306.            mov     al,ah
  307.            out     BASE_CHN,al
  308. ;──────────────────────────────────────────────────────────────────────────────
  309. ; 6th  WRITE BASECOUNTER = SAMPLELENGTH-1
  310. ;
  311.            mov     al,cl
  312.            out     COUNT_CHN,al
  313.            mov     al,ch
  314.            out     COUNT_CHN,al
  315. ;──────────────────────────────────────────────────────────────────────────────
  316. ; 7th  DEMASK CHANNEL
  317. ;
  318.            mov     al,DMAchannel
  319.            out     WRITEMASK,al
  320.  
  321. ;──────────────────────────────────────────────────────────────────────────────
  322. ; Setup SoundBlaster :
  323. ;
  324. ; 1st  SET TIMECONSTANT
  325. ;
  326.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  327.            WAITWRITE
  328.            mov     al,040h                     ;AL = Set timeconstant
  329.            out     dx,al
  330.            WAITWRITE
  331.            mov     al,TIMECONST
  332.            out     dx,al
  333. ;──────────────────────────────────────────────────────────────────────────────
  334. ; 2nd  USE 8bit MONO UNSIGNED MODE (DSPC C6h 10h XX XXh)
  335. ;
  336.            WAITWRITE
  337.            mov     al,0C6h                     ;AL = DMA DAC 8bit autoinit
  338.            out     dx,al
  339.            WAITWRITE
  340.            mov     al,010h                     ;AL = mono signed data
  341.            out     dx,al
  342.            mov     cx,SAMPLEBUFFERLENGTH
  343.            shr     cx,1                        ; generate IRQ every half buffer
  344.            dec     cx
  345.            WAITWRITE
  346.            mov     al,cl                       ;AL = LOWER PART SAMPLELENGTH
  347.            out     dx,al
  348.            WAITWRITE
  349.            mov     al,ch                       ;AL = HIGHER PART SAMPLELENGTH
  350.            out     dx,al
  351.  
  352. ; TRANSFER STARTs.....NOW..... :)
  353. waitloop:  mov     ah,01                       ;AH = Check for character function
  354.            int     016h                        ;   Interrupt: Keyboard
  355.            jz      waitloop                    ; wait for a key (sound in background)
  356.  
  357.            xor     ah,ah                       ;Read character, flush keypress
  358.            int     016h                        ;   Interrupt: Keyboard
  359.            cmp     al,'p'                      ; check for pause key
  360.            je      pause                       ; ok pause playing
  361.            cmp     al,27
  362.            jne     waitloop
  363.            jmp     exit
  364. pause:     ; NOW HALT PLAYING :
  365.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  366.            WAITWRITE
  367.            mov     al,0D0h
  368.            out     dx,al
  369.  
  370.            ; WAIT FOR ANY KEY
  371.            xor     ah,ah                       ;Read character, flush keypress
  372.            int     016h                        ;   Interrupt: Keyboard
  373.  
  374.            ; NOW CONTINUE PLAYING
  375.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  376.            WAITWRITE
  377.            mov     al,0d4h
  378.            out     dx,al
  379.  
  380.            jmp     waitloop
  381.  
  382. exit:      RESET_DSP
  383.  
  384.            ; RESTORE PIC MASK
  385.            in      al,021h
  386.            or      al,PICORMASK                ;<-- SET REGISTER MASK BITS TO DISABLE
  387.            out     021h,al
  388.  
  389.            ; RESTORE IRQ :
  390.            xor     ax,ax
  391.            mov     es,ax                       ; es to page 0 (Interrupt table)
  392.            mov     si,IRQ7*4
  393.            mov     ax,[OLDInterruptOFS]
  394.            mov     es:[si],ax                  ; set old interrupt routine
  395.            mov     ax,[OLDInterruptSEG]
  396.            mov     es:[si+2],ax
  397.  
  398.            ; CLEAR KEYBUFFER
  399.            mov     ah,01
  400.            int     16h
  401.            jz      return2dos
  402.            xor     ah,ah                       ;Read character, flush keypress
  403.            int     016h                        ;   Interrupt: Keyboard
  404.  
  405.            ; TERMINATE EXE:
  406. return2dos:
  407.            mov     ax,04c00h
  408.            int     21h
  409.  
  410. ; display information if Soundblaster is not on this baseaddress
  411. RESET_ERROR:
  412.            mov     dx,offset sberror
  413.            mov     ah,9
  414.            int     21h                         ; text output
  415.            jmp     return2dos
  416.  
  417. ;──────────────────────────────────────────────────────────────────────────────
  418. ; Our own IRQ for detecting buffer half SB currently plays
  419. ; It's generated by the SoundBlaster hardware
  420. ;──────────────────────────────────────────────────────────────────────────────
  421. OWN_IRQ:   pusha
  422.            mov     dx,BASEADDR+00Eh            ;DX = DSP DATA AVAILABLE (IRQ ACKNOWLEDGE)
  423.            in      al,dx
  424.            mov     ax,@data
  425.            mov     ds,ax
  426.            mov     dx,offset txtpart0
  427.            cmp     [part],0
  428.            je      notpart1
  429.            mov     dx,offset txtpart1
  430. notpart1:  mov     ah,9
  431.            int     21h             ; text output
  432.            neg     [part]
  433.            inc     [part]          ; part = 1-part  result : 0,1,0,1,0,....
  434.            mov     al,020h
  435.            out     020h,al                     ;ACKNOWLEDGE HARDWARE INTERRUPT
  436.            popa
  437.            IRET
  438.  
  439. END     __start
  440.